#ifndef STATEMACHINE_H
#define STATEMACHINE_H


#include <cassert>
#include <string>

#include "State.h"


template <class entity_type>
class StateMachine
{
private:

  //a pointer to the agent that owns this instance
  entity_type*          f_pOwner;

  State<entity_type>*   f_pCurrentState;
  
  //a record of the last state the agent was in
  State<entity_type>*   f_pPreviousState;

  //this is called every time the FSM is updated
  State<entity_type>*   f_pGlobalState;
  

public:

  StateMachine(entity_type* owner):f_pOwner(owner),
                                   f_pCurrentState(NULL),
                                   f_pPreviousState(NULL),
                                   f_pGlobalState(NULL)
  {}

  virtual ~StateMachine(){}

  //use these methods to initialize the FSM
  void SetCurrentState(State<entity_type>* s){f_pCurrentState = s;}
  void SetGlobalState(State<entity_type>* s) {f_pGlobalState = s;}
  void SetPreviousState(State<entity_type>* s){f_pPreviousState = s;}
  
  //call this to update the FSM
  void  Update()const
  {
    //if a global state exists, call its execute method, else do nothing
    if(f_pGlobalState)   f_pGlobalState->Execute(f_pOwner);

    //same for the current state
    if (f_pCurrentState) f_pCurrentState->Execute(f_pOwner);
  }

  //bool  HandleMessage(const Telegram& msg)const
  //{
  //  //first see if the current state is valid and that it can handle
  //  //the message
  //  if (f_pCurrentState && f_pCurrentState->OnMessage(f_pOwner, msg))
  //  {
  //    return true;
  //  }
  //
  //  //if not, and if a global state has been implemented, send 
  //  //the message to the global state
  //  if (f_pGlobalState && f_pGlobalState->OnMessage(f_pOwner, msg))
  //  {
  //    return true;
  //  }

  //  return false;
  //}

  //change to a new state
  void  ChangeState(State<entity_type>* pNewState)
  {
    assert(pNewState && 
           "<StateMachine::ChangeState>: trying to change to NULL state");

    //keep a record of the previous state
    f_pPreviousState = f_pCurrentState;

    //call the exit method of the existing state
    f_pCurrentState->Exit(f_pOwner);

    //change state to the new state
    f_pCurrentState = pNewState;

    //call the entry method of the new state
    f_pCurrentState->Enter(f_pOwner);
  }

  //change state back to the previous state
  void  RevertToPreviousState()
  {
    ChangeState(f_pPreviousState);
  }

  //returns true if the current state's type is equal to the type of the
  //class passed as a parameter. 
  bool  isInState(const State<entity_type>& st)const
  {
    return typeid(*f_pCurrentState) == typeid(st);
  }

  State<entity_type>*  CurrentState()  const{return f_pCurrentState;}
  State<entity_type>*  GlobalState()   const{return f_pGlobalState;}
  State<entity_type>*  PreviousState() const{return f_pPreviousState;}
};




#endif